﻿using System;
using System.IO;
using System.Net;

namespace SocialKit.LightRest
{
    /// <summary>
    /// Represents an http response message from a server and 
    /// provides methods to read the response http body.
    /// </summary>
    public class HttpResponseMessage : IDisposable
    {
        HttpWebResponse webResponse;
        bool isConsumed = false;
        bool isBuffered = false;

        byte[] buffer;

        /// <summary>
        /// Gets the method that is used to return the http response.
        /// </summary>
        public string Method { get; private set; }

        /// <summary>
        /// Gets the URI of the Internet resource that responded to the http response.
        /// </summary>
        public Uri ResponseUri { get; private set; }

        /// <summary>
        /// Gets the headers that are associated with the http response.
        /// </summary>
        public WebHeaderCollection Headers { get; private set; }

        /// <summary>
        /// Gets the length of the content returned by the http response.
        /// </summary>
        public long ContentLength { get; private set; }

        /// <summary>
        /// Gets a string value indicates the content type of the http response.
        /// </summary>
        public string ContentType { get; private set; }

        /// <summary>
        /// Gets the status of the http response.
        /// </summary>
        public HttpStatusCode StatusCode { get; private set; }

        /// <summary>
        /// Create an instance of SocialKit.LightRest.HttpResponseMessage class.
        /// </summary>
        protected HttpResponseMessage() { }

        internal HttpResponseMessage(HttpWebResponse webResponse)
        {
            this.webResponse = webResponse;

            this.Method = webResponse.Method;
            this.ResponseUri = webResponse.ResponseUri;
            this.Headers = webResponse.Headers;
            this.ContentLength = webResponse.ContentLength;
            this.ContentType = webResponse.ContentType;
            this.StatusCode = webResponse.StatusCode;
        }

        /// <summary>
        /// Load the response http body into a buffer for multiple uses.
        /// </summary>
        public void LoadContentIntoBuffer()
        {
            ThrowIfConsumedAndNotBuffered();

            if (this.isBuffered)
                return;

            this.buffer = ReadContentAsBytes();
            this.isBuffered = true;
        }

        /// <summary>
        /// Returns a string represents the response http body of this response.
        /// </summary>
        /// <returns>A string.</returns>
        public string ReadContentAsString()
        {
            ThrowIfConsumedAndNotBuffered();

            string content = null;

            if (this.isBuffered)
            {
                content = RestUtility.ExtractEncoding(this.ContentType).GetString(this.buffer, 0, this.buffer.Length);
            }
            else
            {
                using (var stream = webResponse.GetResponseStream())
                {
                    using (var reader = new StreamReader(stream, RestUtility.ExtractEncoding(this.ContentType)))
                    {
                        content = reader.ReadToEnd();

                        CloseWebResponse();
                    }
                }
            }

            return content;
        }

        /// <summary>
        /// Returns a stream represents the response http body of this response message.
        /// </summary>
        /// <returns>A stream.</returns>
        public Stream ReadContentAsStream()
        {
            ThrowIfConsumedAndNotBuffered();

            if (this.isBuffered)
            {
                return new MemoryStream(this.buffer, 0, this.buffer.Length);
            }

            var ms = this.ContentLength < 0 ? new MemoryStream() : new MemoryStream((int)this.ContentLength);

            using (var stream = webResponse.GetResponseStream())
            {
                stream.CopyStreamTo(ms);
            }

            ms.Position = 0;//reset the posotion to the start.

            CloseWebResponse();

            return ms;
        }

        /// <summary>
        /// Returns a byte array represents the response http body of this response message.
        /// </summary>
        /// <returns>A byte array.</returns>
        public byte[] ReadContentAsBytes()
        {
            ThrowIfConsumedAndNotBuffered();

            if (this.isBuffered)
            {
                return this.buffer;
            }

            byte[] temp = null;

            using (var stream = webResponse.GetResponseStream())
            {
                temp = stream.ReadAllBytes();
            }

            CloseWebResponse();

            return temp;
        }

        void ThrowIfConsumedAndNotBuffered()
        {
            if (this.isConsumed && !this.isBuffered)
            {
                throw new InvalidOperationException("The response content has already been consumed and not loaded into the buffer!");
            }
        }

        void CloseWebResponse()
        {
            this.isConsumed = true;
            webResponse.Close();
        }

        #region IDisposable Members

        /// <summary>
        /// Close the WebResponse.
        /// </summary>
        public void Dispose()
        {
            if (this.webResponse != null)
                CloseWebResponse();

            if (this.isBuffered)
                this.buffer = null;
        }

        #endregion

        /// <summary>
        /// Returns the description string of this response message.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("HttpResponseMessage(ContentType=\"{0}\",ContentLength={1})", this.ContentType, this.ContentLength);
        }
    }
}